package com.yogi.webframework.web;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.commons.lang.math.NumberUtils;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.WebUtils;

import com.yogi.webframework.dao.GenericDao;
import com.yogi.webframework.dto.GenericDto;
import com.yogi.webframework.service.GenericService;

public abstract class AbstractCRUDControllerBean<T, E, PK extends Serializable> {

	private static final String MODEL_FORM 	= "modelForm";
	private static final String MODEL_LIST 	= "modelList";
	private static final String MODEL_TITLE	= "modelTitle";
	
	private String title;
	
	public abstract String getViewForm();
	public abstract String getViewList();
	public abstract String getViewListRedirect();	
	public abstract GenericDao<T, Serializable> getDao();
	public abstract GenericService<E, Serializable> getService();

	@SuppressWarnings("unchecked")
	protected Class<T> getEntityClass() {
		return (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	protected T getEntityInstance(){
		try {
			return (T) getEntityClass().newInstance();
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	protected Class<E> getDtoClass() {
		return (Class<E>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[1];
	}

	protected GenericDto getDtoInstance() {
		try {
			return (GenericDto) getDtoClass().newInstance();
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
			
	@RequestMapping(value = "/form.html", method = RequestMethod.GET)
	protected String doPrepareForm(
			@RequestParam(value = "itemId", required = false) Integer id, 
			Model model) {
		GenericDto dto = getDtoInstance();
		String sufixTitle = " - Insert new data";
		if (id != null) {
			sufixTitle = " - Update data";
			if (getDao()!=null){
				final T entity = getDao().findById(id);
				dto.constructDto(entity);
			} else {
				dto = (GenericDto) getService().findById(id);
			}
			
		}
		model.addAttribute(MODEL_FORM, dto);
		
		model.addAttribute(MODEL_TITLE, getTitle() + sufixTitle);
		return getViewForm();
	}

	@RequestMapping(value="/form.html", method=RequestMethod.POST)
	protected String doUpdateOrSave(
			@Valid @ModelAttribute(MODEL_FORM) E dtoModel,
			BindingResult result,
			Model model
			){
		if (result.hasErrors()){
			System.out.println("*******" + result.getErrorCount());
			model.addAttribute(MODEL_FORM, dtoModel);
			return getViewForm();
		}
		
		GenericDto dto = (GenericDto) dtoModel;		
		if (getDao()!=null){
			T entity = getEntityInstance();
			dto.constructModel(entity);
			getDao().save(entity);
		} else {
			getService().save(dtoModel);
		}
		
		return getViewListRedirect();
	}
	
	@RequestMapping(value="/list.html")
	protected String doList(
			Model model,
			HttpServletRequest request			
			){
		
		if (getDao()!=null){
			model.addAttribute(MODEL_LIST, getDao().findAll());
		} else {
			model.addAttribute(MODEL_LIST, getService().findAll());
		}
		
		
		model.addAttribute(MODEL_TITLE, getTitle() + " - List");
		return getViewList();
	}
	
	@RequestMapping(value = "/delete.html", method = RequestMethod.POST)
	protected void doDelete(HttpServletRequest request){
		if (WebUtils.hasSubmitParameter(request, "removeId[]")){
			String[] list = request.getParameterValues("removeId[]");
			Integer id = 0;
			T entity = null;
			E dto = null;
			for (String val:list){
				id = NumberUtils.createInteger(val);
				if (id > 0){
					System.out.println("Deleted id ["+id+"]");
					if (getDao()!=null){
						entity = getDao().findById(id);					
						getDao().delete(entity);
					} else {
						dto = getService().findById(id);
						getService().delete(dto);
					}
//					return "succed";
				}
			}
		}
//		return "empty";
	}
	
	// Getter Setter
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	

}
